﻿#ifndef METACREATOR_H
#define METACREATOR_H

#include <assert.h>
#include <metainfo.h>
#include <signalslot.h>

#include <Reflectable.h>

#include <reflectutils.h>
#include "UniverseFunction.h"

template <typename T>
class MetaCreator;

class StandardMetaInfoBackend;

class REFLECTX_EXPORT __StandardMetaInfoHelper
{
private:
	struct ConstructorInfo {
		std::vector<MetaTypeId> ArgTyps;
		std::function<void(void*buffer, const void** param)> con;
		std::vector<std::string> Infos;
	};

	struct FunctionInfo
	{
		std::string Name;
		std::vector<MetaTypeId> ArgTyps;
		MetaTypeId ReturnType;
		FunctionAny fun;
		std::vector<std::string> Infos;
		MetaFunctionInfo::Flag flags;
	};

	struct PropertyInfo
	{
		std::string Name;
		MetaTypeId Type;
		FunctionAny getter;
		FunctionAny setter;
		std::string Notifier;
		std::vector<std::string> Infos;
		MetaPropertyInfo::Flag flags;
	};

	struct SignalInfo
	{
		std::string Name;
		std::vector<MetaTypeId> ArgTypes;
		std::function<GenericSignal* (void* t)> getter;
		std::vector<std::string> Infos;
	};

	struct ClassData
	{
		IMetaInfoBackend::ClassFlag flag;
		int Size;
		std::string className;

		std::function<void(void*)> Destory;
		std::function<Reflectable*(void*)> Caster;

		std::vector<ConstructorInfo> m_ConstructorInfo;
		std::vector<FunctionInfo> m_FunctionInfo;
		std::vector<SignalInfo> m_SignalInfos;
		std::vector<PropertyInfo> m_PropertyInfos;
		std::vector<std::string> ClassInfo;
	};

	static IMetaInfoBackend* CreateMetaInfo(IMetaInfoBackend* SuperClass, const ClassData& data);

	template <typename T>
	friend class MetaCreator;

	friend class DynamicMetaCreator;

	friend class StandardMetaInfoBackend;

};


template <typename T>
class MetaCreator
{
public:
	MetaCreator(const char* classname) :result{} {
		super = NULL;
		result.className = classname;
		result.Size = sizeof(T);
		result.Destory = [](void* target) {
			auto t = static_cast<T*>(target);
			t->~T();
		};

		RegisterMetaCast<T>();
    }

    MetaCreator(const char* classname,const MetaInfo& Super):MetaCreator(classname){
		super = Super.GetBackend();
    }

    MetaInfo finish(){
		IMetaInfoBackend* backEnd = __StandardMetaInfoHelper::CreateMetaInfo(super, result);
		std::shared_ptr<IMetaInfoBackend> i(backEnd);
		return MetaInfo(backEnd);
    }

public:
    void addDefaultConstructor(){
		__StandardMetaInfoHelper::ConstructorInfo info;

		info.ArgTyps = {};
		info.con = [](void* buffer,const void**param) {
			new (buffer)T();
		};

		result.m_ConstructorInfo.push_back(info);
    }

    void addCopyConstructor(){
		__StandardMetaInfoHelper::ConstructorInfo info;

		info.ArgTyps = {T};
		info.con = [](void* buffer, const void**param) {
			new (buffer)T(*(const T*)(param[0]));
		};

		result.m_ConstructorInfo.push_back(info);
    }

	class ConstructorHelper {
	public:
		ConstructorHelper(__StandardMetaInfoHelper::ClassData* meta) {
			info = {};
			this->meta = meta;
		}

		template<typename ...Args>
		ConstructorHelper& setConstructor() {
			info.ArgTyps = { MetaType::IdFromType<typename NormalizeType<Args>::Type>()... };
			info.con = [=](void* buffer, const void**param) {
				int argCount = sizeof ...(Args);
				new (buffer)T((*(const NormalizeType<Args>::Type*)ParamExpand(argCount, param))...);
			};
			return *this;
		}

		ConstructorHelper& addInfo(const char* e) {
			info.Infos.push_back(e);
			return *this;
		}

		void finish() {
			meta->m_ConstructorInfo.push_back(info);
		}

		__StandardMetaInfoHelper::ConstructorInfo info;
		__StandardMetaInfoHelper::ClassData* meta;
	};

	ConstructorHelper addConstructor() {
		ConstructorHelper helper(&result);
		return helper;
	}

public:
    class PropertyHelper{
    private:
        PropertyHelper(__StandardMetaInfoHelper::ClassData* meta,const char* name){
            info = {};
			info.Name = name;
			info.Type = MetaType::BT_None;

            this->meta = meta;
        }

    public:
		template<typename Func>
		PropertyHelper& addGetter(const Func& func) {
			typedef GetFunctionType<Func> FuncType;
			assert(TypeCheck(MetaType::IdFromType<typename NormalizeType<FuncType::ReturnType>::Type>()) && "Type Error");
			info.getter = WarpFunction(func);
			if (FuncType::Flag != FF_MemberFunction)
			{
				info.flags = MetaPropertyInfo::Flag(info.flags | MetaFunctionInfo::Static);
			}
			return *this;
		}

		template<typename Func>
		PropertyHelper& addSetter(const Func& func) {
			typedef GetFunctionType<Func> FuncType;
			auto argTypes = MetaType::ToTypeList(FuncType::ArgType());
			MetaTypeId ArgId = MetaType::BT_None;
			if (argTypes.size() == 1)
			{
				ArgId = argTypes[0];
			}
			assert(TypeCheck(ArgId) && "Type Error");
			info.setter = WarpFunction(func);
			if (FuncType::Flag != FF_MemberFunction)
			{
				info.flags = MetaPropertyInfo::Flag(info.flags | MetaFunctionInfo::Static);
			}
			return *this;
		}

        PropertyHelper& addNotifier(const char* name){
			info.Notifier = name;
            return *this;
        }

        PropertyHelper& addInfo(const char* m){
			info.Infos.push_back(m);
            return *this;
        }

        void finish(){
			meta->m_PropertyInfos.push_back(info);
        }

    private:
        bool TypeCheck(MetaTypeId id){
            if(id == 0){
                return false;
            }
            if(info.Type == 0){
                info.Type = id;
                return true;
            }

            if(info.Type == id){
                return true;
            }

            return false;
        }

    private:
		__StandardMetaInfoHelper::ClassData* meta;
		__StandardMetaInfoHelper::PropertyInfo info;
        friend class MetaCreator;
    };

    PropertyHelper addProperty(const char* Name){
        PropertyHelper helper(&result,Name);
        return helper;
    }

    class FunctionHelper{
    public:
        FunctionHelper(__StandardMetaInfoHelper::ClassData* meta,const char* name){
            info = {};
			info.Name = name;
            this->meta = meta;
        }

		template<typename Func>
		FunctionHelper&
			setFunction(Func f)
		{
			typedef GetFunctionType<Func> FuncType;
			if (FuncType::Flag != FF_MemberFunction)
			{
				info.flags = MetaFunctionInfo::Flag(info.flags | MetaFunctionInfo::Static);
			}
			
			info.ArgTyps = MetaType::ToTypeList(FuncType::ArgType());
			info.ReturnType = MetaType::IdFromType<FuncType::ReturnType>();
			info.fun = WarpFunction(f);
			return *this;
		}

        FunctionHelper& addInfo(const char* e){
			info.Infos.push_back(e);
            return *this;
        }

        void finish(){
			meta->m_FunctionInfo.push_back(info);
        }

    private:
		__StandardMetaInfoHelper::FunctionInfo info;
        __StandardMetaInfoHelper::ClassData* meta;
    };

    FunctionHelper addFunction(const char* Name){
        FunctionHelper helper(&result,Name);
        return helper;
    }

    class SignalHelper
    {
    public:
        SignalHelper(__StandardMetaInfoHelper::ClassData* meta,const char* name){
            info = {};
			info.Name = name;
            this->meta = meta;
        };

        template<typename T2,typename ...Args>
        SignalHelper&
        setSignal(Signal<Args...> (T2::*signal)){
            info.getter = [=](void* o){
                T* t = (T*)o;
                return &(t->*signal);
            };
            info.ArgTypes = GetTypeList<typename NormalizeType<Args>::Type...>();
            return *this;
        }

        SignalHelper& addInfo(const char* e){
			info.Infos.push_back(e);
            return *this;
        }

        void finish(){
			meta->m_SignalInfos.push_back(info);
        }

    private:
        __StandardMetaInfoHelper::SignalInfo info;
		__StandardMetaInfoHelper::ClassData *meta;
    };

    SignalHelper addSignal(const char* name)
    {
        SignalHelper helper(&result,name);
        return helper;
    }

    void addClassInfo(const char* content){
		result.ClassInfo.push_back(content);
    }

private:
    template<typename T>
    typename std::enable_if<!(std::is_base_of<Reflectable, T>::value || std::is_same<Reflectable, T>::value), void>::type RegisterMetaCast() {

	}

    template<typename T>
    typename std::enable_if<std::is_base_of<Reflectable, T>::value || std::is_same<Reflectable, T>::value, void>::type RegisterMetaCast() {

		result.Caster = [](void* p) {
            T* t = (T*)p;
			Reflectable* r = t;
			return r;
		};

        MetaTypeId id = MetaType::RegisterType<T*>(result.className + "*");
	}

private:
	__StandardMetaInfoHelper::ClassData result;
	IMetaInfoBackend* super;
};

#endif // METACREATOR_H
